/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package neuronalenetzeelearning.PropertyController;

import java.util.LinkedList;
import java.util.Vector;
import javax.swing.table.DefaultTableModel;
import neuronalenetzeelearning.model.Graph;
import neuronalenetzeelearning.model.HebbLearningRule;
import neuronalenetzeelearning.model.LearningRule;
import neuronalenetzeelearning.model.StepFunction;
import neuronalenetzeelearning.model.SupervisedLearningRule;
import neuronalenetzeelearning.view.controlpanel.GraphPanelProperty;
import neuronalenetzeelearning.view.controlpanel.LearningRuleHebbPanel;
import neuronalenetzeelearning.view.controlpanel.LearningRuleSuperwisedLearningPanel;
import neuronalenetzeelearning.view.controlpanel.NStepPanel;
import neuronalenetzeelearning.view.controlpanel.PassPanel;
import neuronalenetzeelearning.view.controlpanel.PropertyGraphPanel;
import neuronalenetzeelearning.view.controlpanel.StepFunktionPanel;
import neuronalenetzeelearning.view.controlpanel.StepwisePanel;

/**
 *
 * @author Henning Ainödhofer
 * 
 */
public class PropertyController {

    LinkedList<String> propertys = new LinkedList<String>();
    /**
     * In dieser Liste werden alle Einstellungen gespeichert.
     */

    private LearningRule lr;
    static int count = 1;
    private StepFunction stepFunktion;
    /**
     * Die Integer-Variable stepFunktion stellt Aktivierungsfunktion dar, mit der die neuen
     * Werte der Knoten berechnet werden. Die Aktivierungsfunktion ist wie folgt definiert:
     * 0 = Linear
     * 1 = Schwellenwert
     */
    private StepFunction outputFunktion;
    /**
     * Die Integer-Variable outputFunktion stellt Ausgabefunktion dar, nach der die Werte der
     * Knoten neu berechnet werden. Die Ausgabefunktion ist wie folgt definiert:
     * 0 = Linear
     * 1 = Sigmoid
     */
    private int nodeNumber;
    /**
     * Die Integer-Variable nodeNumber stellt die Anzahl der Knoten dar, die im Graphen
     * vorhanden sind.
     */
    private int edgeNumber;
    /**
     * Die Integer-Variable edgeNumber stellt die Anzahl der Kanten dar, die im Graphen
     * vorhanden sind.
     */
    private int Stepwidth;
    /**
     * Die Integer-Variable Stepwidth definiert die Schrittweite der Ausführung wie folgt:
     * 2 = Durchlauf = 10000 maximal
     * 1 = N-Schritte = Angabe der Anzahl in n
     * 0 = Schrittweise = ohne Angabe von Schritten
     */
    private double[] thresholdValues;
    /**
     * Das double-Array thresholdValues stellt die Schwellenwerte dar, wobei der Index 0 den
     * Wert für den ersten Knoten im Graphen bezeichnet. Die Knoten müssen dementsprechend
     * durchnummeriert sein.
     */
    private double[] edgeValues;
    /**
     * Das double-Array edgeValues stellt die Kantengewichte dar, wobei der Index 0 die erste
     * Kante im Graphen bezeichnet. Die Kanten müssen dementsprechend durchnummeriert sein.
     */
    private Graph graph;
    /**
     * Die Variable graph stellt das Modell des Graphen dar, der angezeigt wird.
     */
    private int learningRule;
    /**
     * Die Integer-Variable learningRule definiert die Lernregel wie folgt:
     * 0 = Hebb;
     * 1 = Überwachtes Lernen
     */
    private Vector<Vector<Double>> inputVector;
    /**
     * Der Vector inputVecor stellt den Eingabevektor dar, wobei der Index 0 den ersten Knoten
     * in der Eingabeschicht beschreibt.
     */
    private Vector<Vector<Double>> outputVector;
    /**
     * Der Vector outputVector stellt den Ausgabevektor dar, wobei der Index 0 den ersten Knoten
     * in der Ausgabeschicht beschreibt.
     */
    private double LearningRate;
    /**
     * Die Variable LearningRate legt die Lernrate fest. Sie kann Werte zwischen
     * 0 und 1 annehmen. Der Defaultwert ist 0.5
     */
    private int StepMode;

    public int getStepMode() {
        return StepMode;
    }

    public void setStepMode(int StepMode, StepwisePanel stpan, NStepPanel nstpan, PassPanel passpan) {
        this.StepMode = StepMode;
        stpan.setGraph(this.getGraph());
        stpan.updateMatrixComponent(this.getGraph());
        stpan.updateIOComponent(this.getGraph());
        nstpan.setGraph(this.getGraph());
        nstpan.updateMatrixComponent(this.getGraph());
        passpan.setGraph(this.getGraph());
        passpan.updateMatrixComponent(this.getGraph());
    }

    public double[] getEdgeValues() {
        return edgeValues;
    }

    public void setEdgeValues(PropertyGraphPanel panel) {
        DefaultTableModel model = panel.getDefaultEdgeTableModel();
        this.edgeValues = new double[this.graph.getEdges().size()];
        for (int i = 0; i < this.graph.getEdges().size(); i++) {
            this.edgeValues[i] = Double.parseDouble(model.getValueAt(i, 1).toString());
            this.graph.getEdges().get(i).setWeight(this.edgeValues[i]);
        }
    }

    public double getLearningRate() {
        return LearningRate;
    }

    public void setLearningRate(StepFunktionPanel panel, boolean bool) {
        if (bool) this.propertys.clear();
        this.LearningRate = panel.getlearningRateSpinnerValue();
        this.propertys.add("Lernrate: "+this.LearningRate);
    }

    public double[] getThresholdValues() {
        return thresholdValues;
    }

    public void setThresholdValues(PropertyGraphPanel panel) {
        DefaultTableModel model = panel.getDefaultTableModel();
        this.thresholdValues = new double[this.nodeNumber];
        for (int i = 0; i < this.nodeNumber; i++) {
            this.thresholdValues[i] = Double.parseDouble(model.getValueAt(i, 1).toString());
            this.graph.getNodes().get(i).setThreshold(this.thresholdValues[i]);
        }
    }

    public int getStepwidth() {
        return Stepwidth;
    }

    public void setStepwidth(int Stepwidth) {
        this.Stepwidth = Stepwidth;
    }

    public int getEdgeNumber() {
        return edgeNumber;
    }

    public void setEdgeNumber(int edgeNumber) {
        this.edgeNumber = edgeNumber;
    }

    public Graph getGraph() {
        return graph;
    }

    public void setGraph(Graph graph) {
        this.graph = graph;
    }

    public Vector getInputVector() {
        return this.inputVector;
    }

    /**
     * Vector von double Vectoren
     * @param panel
     */
    public void setInputVector(GraphPanelProperty panel) {
        this.inputVector = new Vector<Vector<Double>>();
        DefaultTableModel model = null;
        if (panel instanceof LearningRuleHebbPanel) {
            LearningRuleHebbPanel Hebbpanel = (LearningRuleHebbPanel) panel;
            model = Hebbpanel.getDefaultTableModel();
        } else if (panel instanceof LearningRuleSuperwisedLearningPanel) {
            LearningRuleSuperwisedLearningPanel SVpanel = (LearningRuleSuperwisedLearningPanel) panel;
            model = SVpanel.getDefaultTableModelInput();
        }
        for(int i = 1; i< model.getColumnCount(); i++){
            Vector<Double> possibleVectors = new Vector<Double>();
            for (int j=0; j<model.getRowCount(); j++){
                possibleVectors.add(Double.parseDouble(model.getValueAt(j, i).toString()));

            }
            this.inputVector.add(possibleVectors);
        }
       
    }

    public int getLearningRule() {
        return learningRule;
    }

    public void setLearningRule(GraphPanelProperty panel, boolean bool) {
        if (bool) this.propertys.removeLast();
        if (panel instanceof LearningRuleSuperwisedLearningPanel) {
            this.learningRule = 1;
            this.propertys.add("Lernregel: Überwachtes Lernen");
        } else if (panel instanceof LearningRuleHebbPanel) {
            this.learningRule = 0;
            this.propertys.add("Lernregel: Hebbsche Lernregel");
        }
    }

    public int getNodeNumber() {
        return nodeNumber;
    }

    public void setNodeNumber(int nodeNumber) {
        this.nodeNumber = nodeNumber;
    }

    public Vector getOutputVector() {
        return this.outputVector;
    }

    public void resetCount(){
        count = 1;
    }

    public void setOutputVector(LearningRuleSuperwisedLearningPanel panel) {
        this.outputVector = new Vector<Vector<Double>>();
        DefaultTableModel model = null;
        LearningRuleSuperwisedLearningPanel SVpanel = (LearningRuleSuperwisedLearningPanel) panel;
        model = SVpanel.getDefaultTableModelOutput();
        for(int i = 1; i< model.getColumnCount(); i++){
            Vector<Double> possibleVectors = new Vector<Double>();
            for (int j=0; j<model.getRowCount(); j++){
                possibleVectors.add(((Integer)model.getValueAt(j, i)).doubleValue());
            }
            this.outputVector.add(possibleVectors);
            
        }
    }

    public StepFunction getOutputFunktion() {
        return outputFunktion;
    }
// hier muss man noch gucken, ob alles klappt!!!!

    public void setOutputFunktion(StepFunktionPanel panel, boolean bool) {
        System.out.println("outputFunktion: "+outputFunktion+", getstepFunktion(): "+panel.getstepFunktion()+", getoutputFunktion(): "+panel.getoutputFunktion());

        if (bool) this.propertys.clear();
        if (panel.getstepFunktion() == 0 && panel.getoutputFunktion() == 0) {
            this.outputFunktion = StepFunction.LINEARLINEAR;
            this.propertys.add("Ausgabefunktion: Linear");
        }
        if (panel.getstepFunktion() == 1 && panel.getoutputFunktion() == 0) {
            this.outputFunktion = StepFunction.HEAVISIDELINEAR;
            this.propertys.add("Ausgabefunktion: Schwellenwert");
        }
        if (panel.getstepFunktion() == 2 && panel.getoutputFunktion() == 0) {
            this.outputFunktion = StepFunction.SIGMOIDLINEAR;
            this.propertys.add("Ausgabefunktion: Sigmoid");
        } //        (panel.getstepFunktion() == 1&&panel.getoutputFunktion() == 1)
       // else {
           // this.outputFunktion = StepFunction.HEAVISIDESIGMOID;
       // }
    }

    public StepFunction getStepFunktion() {
        return stepFunktion;
    }

//    public void setStepFunktion(StepFunktionPanel panel) {
//        if (panel.getstepFunktion() == 0){
//            this.stepFunktion = StepFunction.LINEAR;
//        }
//        else {
//            this.stepFunktion = StepFunction.SIGMOID;
//        }
    public void setStepFunktion(StepFunktionPanel panel, boolean bool) {
        System.out.println("outputFunktion: "+outputFunktion+", getstepFunktion(): "+panel.getstepFunktion()+", getoutputFunktion(): "+panel.getoutputFunktion());
        if (bool) this.propertys.clear();
        if (panel.getstepFunktion() == 0 && panel.getoutputFunktion() == 0) {
            this.stepFunktion = StepFunction.LINEARLINEAR;
            this.propertys.add("Aktivierungsfunktion: Linear");
        }
        if (panel.getstepFunktion() == 1 && panel.getoutputFunktion() == 0) {
            this.stepFunktion = StepFunction.HEAVISIDELINEAR;
            this.propertys.add("Aktivierungsfunktion: Schwellenwert");
        }
        if (panel.getstepFunktion() == 2 && panel.getoutputFunktion() == 0) {
            this.stepFunktion = StepFunction.SIGMOIDLINEAR;
            this.propertys.add("Aktivierungsfunktion: Sigmoid");
        } //        (panel.getstepFunktion() == 1&&panel.getoutputFunktion() == 1)
       
    }

    public int RestrictedLearningIteration(int n, PassPanel panel){
        for (int i=1; i<=n; i++){
            System.out.println("hallo");
            panel.updateMatrixComponent(lr.getGraph());
            panel.updateJLabelstep(i);
            if(lr.hasLearned()){
               return i;
            }
        }
        return n;
    }

    public int UnRestrictedLearningIteration(PassPanel panel){
        return RestrictedLearningIteration(1000, panel);
    }

    /**
     * Die Methode calculate benutzt alle zuvor gemachten angaben und wird über die
     * Panels mittels des "Start"-Buttons aufgerufen. Aus dieser Methode heraus werden
     * die Berechnungen angestellt.
     * Pause mit einer if-Anweisung mit ButtonListener
     * Abbruch mit break
     * this.graph = graph;
     */
    public void calculate(GraphPanelProperty panel) {

        NStepPanel nStepPanel = null;
        PassPanel passPanel = null;
        StepwisePanel stepwisePanel = null;
        if (panel instanceof NStepPanel){
            nStepPanel = (NStepPanel) panel;
        }
        else if(panel instanceof PassPanel){
            passPanel = (PassPanel) panel;
        }
        else if (panel instanceof StepwisePanel){
           stepwisePanel = (StepwisePanel) panel;
        }

        if (this.getLearningRule() == 0) {
            lr = new HebbLearningRule(this.graph, this.getLearningRate(), this.getStepFunktion(), this.inputVector);
        } else {
            lr = new SupervisedLearningRule(this.graph, this.getInputVector(), this.getOutputVector(), this.getLearningRate(), this.getStepFunktion());
        }
        if (this.Stepwidth == 0) {
            this.Stepwidth = 100000;
        }

        if (nStepPanel != null) {
            count = lr.RestrictedLearningIteration(this.Stepwidth);
            nStepPanel.updateStepCount();
            nStepPanel.updateMatrixComponent(lr.getGraph(), this.propertys);
        } else if (passPanel != null) {
            count = UnRestrictedLearningIteration(passPanel);
            passPanel.updateJLabelstep(count);
            passPanel.updateMatrixComponent(lr.getGraph(), this.propertys);
        } else {
            lr.nextStep();
            stepwisePanel.updateMatrixComponent(lr.getGraph(), this.propertys);
            stepwisePanel.updateIOComponent(lr.getGraph());
            stepwisePanel.updateJLabelstep(count);
            count++;
        }
        this.setGraph(lr.getGraph());
    }
}
